<!--	
  Loki Sandbox :
  To support LokiJS project at  : https://github.com/techfort/LokiJS

  This web page is designed to allow you to experiment with loki and/or inspect existing saved loki database files.

  You can experiment, running queries on the default sample database or load a saved database from
  your filesystem, and inspect and/or run queries on it to inspect results.
-->
<!DOCTYPE html>
<html>
<head>
    <title>Loki Sandbox</title>

    <script src="../../src/lokijs.js"></script>
    <script src="../../src/loki-indexed-adapter.js"></script>
    <script src="jquery-2.1.0.min.js"></script>
    <script src="sandbox.min.js"></script>
    <link rel="stylesheet" href="sandbox.min.css">

    <style>
      body {
          margin: 0;
          font-family: 'Segoe UI', 'sans-serif';
      }
      a:link
      {
        color: #99F;
        text-decoration: none;
        font-size: 14pt;
      }
      a:hover
      {
        color: #77F;
        text-decoration: underline;
      }
      a:visited
      {
        color: #99F;
      }
      
      select {
        font-size:12pt;
      }
      
      input.vpatel[type=radio], input.vpatel[type=checkbox] {
        display:none;
      }

      input.vpatel[type=radio] + label, input.vpatel[type=checkbox] + label {
        display:inline-block;
        margin:-2px;
        padding: 4px 12px;
        margin-bottom: 0;
        font-size: 14px;
        line-height: 20px;
        color: #333;
        text-align: center;
        text-shadow: 0 1px 1px rgba(255,255,255,0.75);
        vertical-align: middle;
        cursor: pointer;
        background-color: #f5f5f5;
        background-image: -moz-linear-gradient(top,#fff,#e6e6e6);
        background-image: -webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));
        background-image: -webkit-linear-gradient(top,#fff,#e6e6e6);
        background-image: -o-linear-gradient(top,#fff,#e6e6e6);
		    background-image: linear-gradient(to bottom,#fff,#e6e6e6);
		    background-repeat: repeat-x;
		    border: 1px solid #ccc;
		    border-color: #e6e6e6 #e6e6e6 #bfbfbf;
		    border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
		    border-bottom-color: #b3b3b3;
		    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);
		    filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
		    -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
		    -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
		    box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
      }

      input.vpatel[type=radio]:checked + label, input.vpatel[type=checkbox]:checked + label{
        background-image: none;
		    outline: 0;
		    -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
		    -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
		    box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
			    background-color:#c8c8c8;
      }
      button.minimal {
        background: #e3e3e3;
        border: 1px solid #bbb;
        -webkit-border-radius: 3px;
        -moz-border-radius: 3px;
        border-radius: 3px;
        -webkit-box-shadow: inset 0 0 1px 1px #f6f6f6;
        -moz-box-shadow: inset 0 0 1px 1px #f6f6f6;
        box-shadow: inset 0 0 1px 1px #f6f6f6;
        color: #333;
        font: bold 20px "helvetica neue", helvetica, arial, sans-serif;
        line-height: 1;
        padding: 8px 0 9px;
        text-align: center;
        text-shadow: 0 1px 0 #fff;
        width: 150px; 
      }
      button.minimal:hover {
        background: #d9d9d9;
        -webkit-box-shadow: inset 0 0 1px 1px #eaeaea;
        -moz-box-shadow: inset 0 0 1px 1px #eaeaea;
        box-shadow: inset 0 0 1px 1px #eaeaea;
        color: #222;
        cursor: pointer; 
      }
      button.minimal:active {
        background: #d0d0d0;
        -webkit-box-shadow: inset 0 0 1px 1px #e3e3e3;
        -moz-box-shadow: inset 0 0 1px 1px #e3e3e3;
        box-shadow: inset 0 0 1px 1px #e3e3e3;
        color: #000; 
      }
    </style>
</head>

<body style="background-color:#444; color:#fff">

  <h1 style="font-family:'Segoe UI', 'sans-serif'">Loki Sandbox</h1>

  <div id="divLokiCode">
  </div>

  <div id="divLoader" style="position:absolute; top:10px; right: 420px">
    <i>Import database : </i> <input style='height:30px; width:200px;' id='file-import' type="file" onchange="dbImport()" />
  </div>

  <div style="position: absolute; top:10px; right:10px">
    <input class="vpatel" type="radio" id="radio1" name="radios" value="all" checked onclick="tabMode(1)">
    <label for="radio1">Text Output</label>
    <input class="vpatel" type="radio" id="radio2" name="radios" value="false" onclick="tabMode(2)">
    <label for="radio2">Object Inspector</label>
    <input class="vpatel" type="radio" id="radio3" name="radios" value="true" onclick="tabMode(3)">
    <label for="radio3">Save / Load</label>
    <input class="vpatel" type="radio" id="radio4" name="radios" value="true" onclick="tabMode(4)">
    <label for="radio4">About</label>
  </div>

  <table width="100%" style="table-layout:fixed">
    <tr>
      <td colspan="2" align='right'>
        <label>Github sample gists : </label>
        <select id="selSamples" style="min-width:200px" onchange="selSamplesChanged()">
          <option value=""></option>
          <option value="https://gist.githubusercontent.com/obeliskos/f62fc11049c6264f90951fad6d3c487b/raw/71c57b9b3643ab0a94bd96801f9c3c00b6ea80bf/web-quickstart1">Quickstart 1</option>
          <option value="https://gist.githubusercontent.com/obeliskos/3acee5f8d70e24d66fe306bfdcd724d1/raw/2ebdb7df0ccc3097de2216cd84c1dcf89d29e8ee/web-quickstart2">Quickstart 2</option>
          <option value="https://gist.githubusercontent.com/obeliskos/b3b50f3589596218da68b6252ed236bd/raw/6a6349810f8af36abb0e0c82d11e473318a9606b/web-quickstart3">Quickstart 3</option>
          <option value="https://gist.githubusercontent.com/obeliskos/fa0bda5c722bf225c6527c5187ab1ea7/raw/d51d09ba05f6d7eda7b7364c6748b413ce1e344c/web-quickstart4">Quickstart 4</option>
          <option value="https://gist.githubusercontent.com/obeliskos/32e0e730e1d2e1ac5e84f7363331b498/raw/267b2f8023499208ed06514524921acbc1841fcc/web-quickstart-core">Quickstart (Core)</option>
          <option value="https://gist.githubusercontent.com/obeliskos/7c52ded5a5bffd68c1ca2e0b91980173/raw/26c86e0c1ced72993dafae6202dc881b24a3330b/web-quickstart-chaining">Quickstart (Chaining)</option>
          <option value="https://gist.githubusercontent.com/obeliskos/59c4c64e3b8c4609fbf2d7ba9fc0264a/raw/fe5e8291a5af21395a7282ddc9d329f7e163803a/web-quickstart-dynview">Quickstart (DynView)</option>
          <option value="https://gist.githubusercontent.com/obeliskos/4a7a3db8f46c49fd051c54af0104da22/raw/266faaa464bb78635f36ef29414b1acff2791d97/web-quickstart-transforms">Quickstart (Transforms)</option>
          <option value="https://gist.githubusercontent.com/obeliskos/6335093d15e2f12838e642248a8179b9/raw/c0828d72a32130cde5a58c916c4dcc653a1dbb79/loki%2520sort%2520order">Loki Sort Ordering</option>
        </select>
      </td>
      
    <tr>
      <td width="50%" valign="top">
        <h2 style="font-family:'Segoe UI', 'sans-serif'">Sandbox Editor</h2>
        <div id="divLokiEditor">
          <textarea id="jsedit"></textarea>
          <br />
          <button class="minimal" style="width:140px" onclick="runloki();">Run</button>
        </div>
      </td>
      <td width="50%" valign="top">
        <div id="divTextOutput">
          <h2 style="font-family:'Segoe UI', 'sans-serif'">Text Output</h2>
          
          <div id="divText">
            <textarea id="lsoutput"></textarea>
          </div>
        </div>
        <div id="divInspector" style="display:none">
          <h2>Object Inspector</h2>
          <div id="divInspect" style="overflow-y:auto">
            <div id="divPrettyPrint">
            </div>
          </div>
          <br/>
          <button class="minimal" onclick="console.log(db)" style="width:220px">Log db to Console</button>
        </div>
        <div id="divSaveLoad" style="display:none;">
          <h2>Save / Load</h2>
          <br/>
          <table width="100%">
            <tr>
              <td valign="top">
                <label>Script name : </label><br/>
                <input id="txtScriptName" type='text' style="font-size:16px; height:24px; width:200px" value="lokiscript1"/><br/><br/>
                <button class="minimal" onclick="saveLokiScript()">Save</button>
              </td>
              <td valign="top">
                <label>Stored Scripts :</label><br/>
                <select id="selScriptList" size=8 style="font-size:16pt; min-width:200px"></select>
                <br/><br/>
                <button class="minimal" onclick="loadLokiScript()">Load</button><br/><br/>
                <button class="minimal" onclick="deleteLokiScript()">Delete</button>
              </td>
            </tr>
          </table>
        </div>
        <div id="divAbout" style="display:none;">
          <h2>About Loki Sandbox 0.2.0</h2>
          <p style="font-size:18px">
              Loki Sandbox is a simple utility web page which lets you experiment with LokiJS using a browser environment.
              An example database is created by default (in memory), along with sample javascript interaction to populate, query, and sandbox logging capabilities.
          </p>
          <p style="font-size:18px">
            If you have databases which have been saved to the filesystem (from node.js, node webkit, or cordova), you load load them and inspect and/or
            query their contents.
          <p style="font-size:18px">
            This utility page is to support <a href="http://lokijs.org" target="_blank">LokiJS</a>.<br /><br/>
            You can also view the <a href="https://github.com/techfort/LokiJS" target="_blank">GitHub page</a>.
          </p>
          <p>
            Some example logging commands are :<br/>
            <code>
              logText(message);<br/>
              logObject(obj);<br/>
              inspectObject(obj);<br/>
              alertify.log(message);<br/><br/>
            </code>
            alertify is a bundled library, click <a href="http://fabien-d.github.io/alertify.js/" target="_blank">here</a> to learn more of its capability.
          </p>
        </div>
        <br/>
      </td>
    </tr>
  </table>

  <script>
    var db = new loki('sandbox.db');
    
    // we will use loki indexed adapter as a generic app/key/value store
    // we will store scripts instead of serialized loki databases in our 'sandbox' 'app'
    var sandboxAdapter = null;
    if (window && window.indexedDB) {
      sandboxAdapter = new LokiIndexedAdapter('sandbox', { closeAfterSave: true });
    }

    var sbv = {
      editorJS: null,
      editorOutput: null,
    }
    
    function selSamplesChanged() {
      var url = $("#selSamples").val();
      if (url && url !== "") {
        clearOutput();
        loadGist(url);
      }
    }

    // used to retrieve hash (#) parameters 
    function getHashParameter (sParam) {
      var sPageURL = window.location.hash.substring(1);

      var sURLVariables = sPageURL.split('&');
      for (var i = 0; i < sURLVariables.length; i++) {
        var sParameterName = sURLVariables[i].split('=');
        if (sParameterName[0] === sParam) {
          return decodeURIComponent(sParameterName[1]);
        }
      }
    }

/*    
    window.onhashchange = function () {
        // Probably better (cleaner) to just clean slate and force page reload
        location.reload();
    };
*/    
    // let's user go to different tabs via 
    function tabMode(mode) {
      $("#divTextOutput").hide();
      $("#divInspector").hide();
      $("#divSaveLoad").hide();
      $("#divAbout").hide();

      switch(mode) {
        case 1 : $("#divTextOutput").show(); break;
        case 2 : $("#divInspector").show(); break;
        case 3 : $("#divSaveLoad").show(); break;
        case 4 : $("#divAbout").show(); break;
      }
    }

    $(document).ready(function () {
      // Help user quickly identify errors in their code by displaying alert with msg, line and col
      window.onerror = function (msg, url, line, col, error) {
          alertify.error(msg + " (line " + line + " col " + col + ")");
      };

      $(window).resize(function () {
          if (typeof (windowResize) == typeof (Function)) windowResize();
      });

      $("body").css("background-color", "#222");

      setupEditor();

      if (sandboxAdapter) {
        loadScriptList();
      }
      
      // if the user specifies a rawgist hash parameter, attempt to load and run that gist (url)
      var rawgist = getHashParameter("rawgist");
      if (rawgist && rawgist !== "") {
        loadGist(rawgist);
      }
      else {
        setTimeout(function() {
          windowResize();
          runloki();
        }, 250);
      }
    });
    
    function loadGist(rawgist) {
        if (rawgist.indexOf("https://gist.githubusercontent.com") !== 0) {
          console.log("only allowing ajax fetch to gist.githubusercontent.com");
        }

        console.log ("loading rawgist : " + rawgist);

        var oReq = new XMLHttpRequest();
        oReq.addEventListener("load", gistLoaded);
        oReq.open("GET", rawgist);
        oReq.send();
    }

    function loadScriptList() {
      if (!sandboxAdapter.checkAvailability()) {
        return;
      }
      
      sandboxAdapter.getKeyList(function(results) {
        $("#selScriptList").empty();
        
        results.forEach(function(name) {
          $("#selScriptList").append($("<option></option>").text(name).val(name));
        });
      });
    }

    function loadLokiScript() {
      var selectedScript = $("#selScriptList").val();
      
      sandboxAdapter.loadKey(selectedScript, function (result) {
        if (result !== null) {
          $("#txtScriptName").val(selectedScript);
          
          sbv.editorJS.setValue(result);
        }
      });
    }

    function saveLokiScript() {
      var scriptName = $("#txtScriptName").val();


      var scriptText = sbv.editorJS.getValue();

      sandboxAdapter.saveKey(scriptName, scriptText, function() {
        alertify.success("saved");
        loadScriptList();
      });
    }
    
    function deleteLokiScript() {
      var selectedScript = $("#selScriptList").val();

      // probably should have made this method async with callback, for now will use settimeout
      sandboxAdapter.deleteKey(selectedScript);
      alertify.log("deleted");
      setTimeout(loadScriptList, 200);
    }

    function clearOutput() {
      //sbv.editorJS.setValue("");
      sbv.editorOutput.setValue("Click run to launch script");
      //setTimeout(windowResize, 100);
    }

    function gistLoaded () {
      sbv.editorJS.setValue(this.responseText);
      
      setTimeout(windowResize, 100);
    }
    
    function windowResize() {
      setTimeout(function() {
        try {
          sbv.editorJS.setSize("100%", $(window).height() - 270);
          sbv.editorOutput.setSize("100%", $(window).height() - 270);
        }
        catch(err) {
        }
      }, 200);

      $("#divInspect").height($(window).height() - 250);
    }

    // event handler called when user picks file to import
    function dbImport()	{
      var file = document.getElementById('file-import').files[0];
      if(file) {
        var reader = new FileReader();

        reader.readAsText(file, "UTF-8");

        reader.onload = dbLoaded;
        reader.onerror = errorHandler;
      }
    }

    // callback event fired when after file import is done
    function dbLoaded(evt) {
      var filename = $("#file-import").val().replace(/^.*[\\\/]/, '');
      var filestring = evt.target.result;

      $("#divLokiCode").empty();
      sbv.editorJS.setValue('logDatabase();\r\ninspectObject(db);');

      db = new loki('sandbox');
      db.loadJSON(filestring);

      var control = $("#file-import");
      control.replaceWith( control = control.clone( true ) );

      runloki();
    }

    function setupEditor() {
      sbv.editorJS = CodeMirror.fromTextArea(document.getElementById("jsedit"), {
        smartIndent: false,
        lineNumbers: true,
        theme: "pastel-on-dark",
        mode: "javascript",
        foldGutter: true,
        gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
        extraKeys: {
          "Ctrl-Q": function (cm) {
              cm.foldCode(cm.getCursor());
          },
          "F11": function (cm) {
              cm.setOption("fullScreen", !cm.getOption("fullScreen"));
          },
          "Esc": function (cm) {
              if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
          }
        }
      });

      sbv.editorOutput = CodeMirror.fromTextArea(document.getElementById("lsoutput"), {
        smartIndent: false,
        lineNumbers: true,
        theme: "pastel-on-dark",
        mode: "javascript",
        foldGutter: true,
        gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
        extraKeys: {
          "Ctrl-Q": function (cm) {
              cm.foldCode(cm.getCursor());
          },
          "F11": function (cm) {
              cm.setOption("fullScreen", !cm.getOption("fullScreen"));
          },
          "Esc": function (cm) {
              if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
          }
        }
      });

      var initCode = "var db = new loki('sandbox.db');\r\n\r\n";
      initCode += "// Add a collection to the database\r\n";
      initCode += "var items = db.addCollection('items');\r\n\r\n";
      initCode += "// Add some documents to the collection\r\n";
      initCode += "items.insert({ name : 'mjolnir', owner: 'thor', maker: 'dwarves' });\r\n";
      initCode += "items.insert({ name : 'gungnir', owner: 'odin', maker: 'elves' });\r\n";
      initCode += "items.insert({ name : 'tyrfing', owner: 'Svafrlami', maker: 'dwarves' });\r\n";
      initCode += "items.insert({ name : 'draupnir', owner: 'odin', maker: 'elves' });\r\n\r\n";
      initCode += "// Find and update an existing document\r\n";
      initCode += "var tyrfing = items.findOne({'name': 'tyrfing'});\r\n";
      initCode += "tyrfing.owner = 'arngrim';\r\n";
      initCode += "items.update(tyrfing);\r\n\r\n";
      initCode += "// These statements send to Text Output\r\n";
      initCode += "logText('tyrfing value :');\r\n";
      initCode += "logObject(tyrfing);\r\n";
      initCode += "logText('odins items');\r\n";
      initCode += "logObject(items.find({ 'owner': 'odin' }));\r\n\r\n";
      initCode += "// This statement sends to Inspector\r\n";
      initCode += "inspectObject(db);\r\n";

      sbv.editorJS.setValue(initCode);
      sbv.editorOutput.setValue("Welcome to loki sandbox 0.1.0\r\nClick 'Run' to launch your script.");
    }

    var sandbox = {
      // Dump database as json to output
      logDatabase : function() {
        logObject(db, false);
      },
      // Dump message to output
      logText : function(message) {
        var oldText = sbv.editorOutput.getValue();
        oldText += message + "\r\n";
        sbv.editorOutput.setValue(oldText);
      },
      // Dump object as serialized json to output
      logObject : function(obj, isPretty) {
        if (typeof(isPretty) === 'undefined') {
          isPretty = true;
        }
        
        var oldText = sbv.editorOutput.getValue();
        oldText += (isPretty)?(JSON.stringify(obj, undefined, 2) + "\r\n"):(JSON.stringify(obj) + "\r\n");
        
        sbv.editorOutput.setValue(oldText);
      },
      // send an object to the inspector
      inspectObject : function(obj) {
        $("#divInspect").empty();
        var tbl = prettyPrint(obj, { /* options such as maxDepth, etc. */ });
        $("#divInspect").append(tbl);
      }
    };

    // alias globals for b/w compat
    var logDatabase = sandbox.logDatabase;
    var logText = sandbox.logText;
    var logObject = sandbox.logObject;
    var inspectObject = sandbox.inspectObject;

    function runloki() {
        $("#divLokiCode").empty();
        sbv.editorOutput.setValue('');

        var s = document.createElement("script");
        var ls = sbv.editorJS.getValue();

        s.innerHTML = ls;

        tabMode(1);

        // give dom a chance to clean out by waiting a bit?
        setTimeout(function () {
            document.getElementById("divLokiCode").appendChild(s);
        }, 250);

    }

    function errorHandler(evt) {
        alertify.error('load error');
    }

    function toggleVisibility(id) {
        var e = document.getElementById(id);
        e.style.display = (e.style.display == 'block')?'none':'block';
    }

    var hookScripts = function (url, src) {
        var s = document.createElement("script");
        s.type = "text/javascript";
        s.id = 'scriptDynamic';
        s.src = url || null;
        s.innerHTML = src || null;
        document.getElementsByTagName("head")[0].appendChild(s);
    };


  </script>
</body>
</html>
